package ithink.link.service;

import ithink.link.base.message.LinkMessage;
import ithink.link.ConfigHelper;
import ithink.link.bean.DeviceInfo;
import ithink.link.dao.ItkMemcached;
import ithink.link.exception.ParameterErrorException;
import ithink.link.exception.ReconnectException;
import ithink.link.stack.IConnection;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zaqb on 17-10-26.
 */
public class DeivceManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(LinkService.class);
    private Map<String, DeviceInfo> devicesMap = new HashMap<>();
    private String serverName = "";
    final Object devicesMapObj = new Object();
    ItkMemcached memcached = null;
    public DeivceManager(String serverName) throws IOException {
        this.serverName = serverName;
        memcached = new ItkMemcached( ConfigHelper.getMemcachedHost(),
                ConfigHelper.getMemcachedPort(),
                ConfigHelper.getMemcachedUsername(), ConfigHelper.getMemcachedPassword());
    }
    public void deviceLogin(IConnection connection, LinkMessage message) throws ParameterErrorException {
        String sid = message.getText().getString("sid");
        if(StringUtils.isBlank(sid)) {
            throw new ParameterErrorException("device login get blank sid, text = " + message.getText().toJSONString());
        }
        connection.setContext(sid);
        synchronized (devicesMapObj){
            DeviceInfo deviceInfo = devicesMap.computeIfAbsent( sid, k -> new DeviceInfo() );
            if(deviceInfo.getConnection() != null && deviceInfo.getConnection() != connection) {
                //reconnect
                LOGGER.info( "{} reconnect close old", sid );
                deviceInfo.getConnection().disconnect(new ReconnectException("sid " + sid +"  reconnect"));
            }
            deviceInfo.update( connection, message.getText() );
            deviceInfo.setServerName( serverName );
            memcached.set("link_" + sid, serverName, ConfigHelper.getMemcachedExpire());
            updateCurrentCount();
        }
        LOGGER.info("{} login", sid);
    }

    public void deviceLogout(IConnection connection) {
        if(connection.getContext() == null) {
            return;
        }
        String sid = (String)connection.getContext();
        synchronized (devicesMapObj){
            devicesMap.remove(sid);
        }
        memcached.del("link_" + sid);
        updateCurrentCount();
        LOGGER.info("logout", sid);
    }

    public void heartbeat(IConnection connection) {
        if(connection.getContext() == null) {
            return;
        }

        String sid = (String)connection.getContext();
        memcached.set("link_" + sid, serverName, ConfigHelper.getMemcachedExpire());
    }

    public void checkPortMapping(IConnection connection, LinkMessage message) {
        DeviceInfo info = getDeviceInfo(connection);
        if(info == null) {
            LOGGER.error( "get null info from connection:", connection );
            return;
        }
        String ip = info.getExtIp();
        Integer port = Integer.parseInt(info.getTcpPort());
        PortMappingHelper.connect( ip, port, new PortMappingHelper.IListener(info.getSid()){
            @Override
            public void onSuccess() {
                LOGGER.info("{} check port mapping success", info.getSid());
                info.setIsPortMapping("1");
            }

            @Override
            public void onFailed() {
                LOGGER.info("{} check port mapping failed", info.getSid());
                info.setIsPortMapping("0");
            }
        } );
    }

    public void deleteDevice(String sid) {
        DeviceInfo info = getDeviceInfo(sid);
        if(info != null) {
            LOGGER.info("delete device sid {}", sid);
            synchronized (devicesMapObj){
                info.getConnection().disconnect( null );
                devicesMap.remove(sid);
            }
        }
        memcached.del("link_" + sid);
        updateCurrentCount();
    }

    public DeviceInfo getDeviceInfo(String sid) {
        synchronized (devicesMapObj){
            return devicesMap.get(sid);
        }
    }

    public DeviceInfo getDeviceInfo(IConnection connection) {
        if(connection.getContext() == null) {
            return null;
        }
        String sid = (String)connection.getContext();
        synchronized (devicesMapObj){
            return devicesMap.get(sid);
        }
    }

    private void updateCurrentCount() {
        memcached.set(serverName + "_count",
                String.valueOf(devicesMap.size()), -1);
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }
}
